package edu.pku.sei.modeler.uml.commands.component;

import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.commands.Command;

import edu.pku.sei.modeler.exceptions.FactoryNotFoundException;
import edu.pku.sei.modeler.model.common.AbstractFactory;
import edu.pku.sei.modeler.model.common.ModelRegistry;
import edu.pku.sei.modeler.model.structure.MetaDiagram;
import edu.pku.sei.modeler.model.structure.MetaLink;
import edu.pku.sei.modeler.model.structure.MetaNode;
import edu.pku.sei.modeler.model.structure.StructureFactory;
import edu.pku.sei.modeler.model.structure.StructurePackage;
import edu.pku.sei.modeler.model.uml.BasicComponents.Component;
import edu.pku.sei.modeler.model.uml.Dependencies.Realization;
import edu.pku.sei.modeler.model.uml.Dependencies.Usage;
import edu.pku.sei.modeler.model.uml.Interfaces.Interface;
import edu.pku.sei.modeler.uml.commands.UMLConnectionCreateCommand;
import edu.pku.sei.modeler.uml.commands.UMLElementCreateCommand;
import edu.pku.sei.modeler.uml.modelHelper.ModelPackage;

public class NestedInterfaceCreateCommand extends Command {
	private MetaNode componentNode;
	private MetaNode interfaceNode;
	private Point location;
	private Rectangle constraint;

	public NestedInterfaceCreateCommand(MetaNode node, MetaNode hostNode,
			Rectangle constraint) {
		this.componentNode = hostNode;
		this.interfaceNode = node;
		this.constraint = constraint;
		this.location = constraint.getLocation();
	}

	public boolean canExecute() {
		return true;
	}

	public void execute() {
		addNode();
		createAddLink();
	}

	public void addNode() {
		Interface i = (Interface) interfaceNode.getModelElement();
		Component parentComponent = (Component) componentNode.getModelElement();
		
		if (i.getType() == Interface.TYPE_INTERFACE || i.getType() == Interface.TYPE_COMPONENTINTERFACE) {
			Command nodeCreateCmd = new NestedComponentElementCreateCommand(
					interfaceNode, (MetaNode) componentNode, this.constraint);
			if (nodeCreateCmd.canExecute())
				nodeCreateCmd.execute();

		} else {
			if (i.getType() == Interface.TYPE_USEINTERFACE)
				parentComponent.getRequired().add(i);
			else if (i.getType() == Interface.TYPE_IMPLEMENTINTERFACE)
				parentComponent.getProvided().add(i);
			
			if (componentNode.getContainer() instanceof MetaDiagram) {
				UMLElementCreateCommand nodeCreateCmd = new UMLElementCreateCommand(
						interfaceNode, (MetaDiagram) componentNode
								.getContainer(), getInterfaceBound(60, 20));
				if (nodeCreateCmd.canExecute())
					nodeCreateCmd.execute();
			} else if (componentNode.getContainer() instanceof MetaNode) {
				Command nodeCreateCmd = new NestedComponentElementCreateCommand(
						interfaceNode, (MetaNode) componentNode.getContainer(),
						getInterfaceBound(60, 30));
				if (nodeCreateCmd.canExecute())
					nodeCreateCmd.execute();

			}
		}
	}

	public Rectangle getInterfaceBound(int w, int h) {
		Rectangle bounds = new Rectangle();
		bounds.width = w;
		bounds.height = h;
		int centerX, centerY, offsetX, offsetY;
		centerX = componentNode.getWidth() / 2;
		centerY = componentNode.getHeight() / 2;

		offsetX = location.x - centerX;
		offsetY = location.y - centerY;

		if (offsetY >= 0) {
			// right
			if (offsetX > offsetY) {
				bounds.x = componentNode.getLocationX()
						+ componentNode.getWidth() + 40;
				bounds.y = componentNode.getLocationY()
						+ componentNode.getHeight() / 2 - bounds.height / 2;
			} else if (Math.abs(offsetX) > offsetY) {// left
				bounds.x = componentNode.getLocationX() - 40 - bounds.width / 2;
				bounds.y = componentNode.getLocationY()
						+ componentNode.getHeight() / 2 - bounds.height / 2;
			} else {// top
				bounds.x = componentNode.getLocationX()
						+ componentNode.getWidth() / 2 - bounds.width / 2;
				bounds.y = componentNode.getLocationY()
						+ componentNode.getHeight() + 40;

			}
		} else {
			if (offsetX > Math.abs(offsetY)) {// right
				bounds.x = componentNode.getLocationX()
						+ componentNode.getWidth() + 40;
				bounds.y = componentNode.getLocationY()
						+ componentNode.getHeight() / 2 - bounds.height / 2;
			} else if (Math.abs(offsetX) > Math.abs(offsetY)) {// left
				bounds.x = componentNode.getLocationX() - 40 - bounds.width / 2;
				bounds.y = componentNode.getLocationY()
						+ componentNode.getHeight() / 2 - bounds.height / 2;
			} else {// top
				bounds.x = componentNode.getLocationX()
						+ componentNode.getWidth() / 2 - bounds.width / 2;
				bounds.y = componentNode.getLocationY() - 40 - bounds.height
						/ 2;
			}
		}
		return bounds;

	}

	public void createAddLink() {
		try {
			AbstractFactory factory = ModelRegistry.getRegistry().getFactory(
					componentNode.getModel());
			StructureFactory sfactory = (StructureFactory) ModelRegistry
					.getRegistry()
					.getStructureFactory(componentNode.getModel());
			Interface interfaceModel = (Interface) interfaceNode
					.getModelElement();

			if (interfaceModel.getType() == Interface.TYPE_USEINTERFACE) {
				Usage usage = (Usage) factory.createElement(
						ModelPackage.__USAGEWITHOUTPORT__, true);
				MetaLink link = (MetaLink) sfactory.createElement(
						StructurePackage.__LINK__, true);
				link.setModelElement(usage);

				UMLConnectionCreateCommand cmd = ComponentDiagramConnectionCreateFactory.INSTANCE
						.createCommand(link, componentNode);
				cmd.setTarget(interfaceNode);
				if (cmd.canExecute())
					cmd.execute();
			} else if (interfaceModel.getType() == Interface.TYPE_IMPLEMENTINTERFACE) {
				Realization realization = (Realization) factory.createElement(
						ModelPackage.__REALIZATIONWITHOUTPORT__, true);
				MetaLink link = (MetaLink) sfactory.createElement(
						StructurePackage.__LINK__, true);
				link.setModelElement(realization);

				UMLConnectionCreateCommand cmd = ComponentDiagramConnectionCreateFactory.INSTANCE
						.createCommand(link, componentNode);
				cmd.setTarget(interfaceNode);
				if (cmd.canExecute())
					cmd.execute();
			}
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FactoryNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

}
